package exDraw;

import android.content.Context;
import android.graphics.Camera;
import android.graphics.Matrix;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.animation.Transformation;
import android.widget.RelativeLayout;


class SpinLayout extends RelativeLayout
{
	private double nowRadian;
	private double subRadian;

	private float startX;
	private float startY;

	private float beforeX;
	private float beforeY;

	int childAxisX = 0;
	int childAxisY = 0;

	private double sumRadian;

	private VelocityTracker vTracker = null;

	private Context mContext;

	private OnItemSelectedListener mOnItemSelectedListener;

	double mMaxDegree = 90.0;

	double mUnitDegree = 30.0;

	double mClickDistance = 10.0;

	public SpinLayout(Context c) {
		// TODO Auto-generated constructor stub
		this(c,null);
	}
	public SpinLayout(Context c, AttributeSet attr){
		super(c, attr);
		setStaticTransformationsEnabled(true);
		mContext = c;
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		float nowX;
		float nowY;

		// TODO Auto-generated method stub
		int action = event.getAction();

		if(this.vTracker == null){
			this.vTracker = VelocityTracker.obtain();
		}else{
			this.vTracker.clear();
		}

		switch (action){

			case MotionEvent.ACTION_MOVE:

			case MotionEvent.ACTION_DOWN:
				nowX = event.getX();
				nowY = event.getY();

				if(action == MotionEvent.ACTION_DOWN){
					startX = event.getX();
					startY = event.getY();
					initTouchEvent(event);
				}

				nowRadian = Math.toRadians(getRotatedDegree(beforeX, beforeY, nowX, nowY, childAxisX, childAxisY));
				subRadian = nowRadian;
				sumRadian += subRadian;


				if(!isValidArea(Math.toDegrees(sumRadian), -mMaxDegree, 0.0)){
					initTouchEvent(event);

					if(Math.toDegrees(sumRadian) > 0.0){
						subRadian = subRadian - sumRadian;
						sumRadian = 0.0;
					}else{
						subRadian = subRadian + (Math.toRadians(-mMaxDegree) - sumRadian);

						sumRadian = Math.toRadians(-mMaxDegree);
					}
					doBringToFront(Math.toDegrees(sumRadian), 0.5);
					this.invalidate();
					break;
				}
				doBringToFront(Math.toDegrees(sumRadian), 0.5);
				this.invalidate();

				beforeX = nowX;
				beforeY = nowY;

				break;

			case MotionEvent.ACTION_UP:
				double destDegree = 0.0;

				nowX = event.getX();
				nowY = event.getY(); 

				if(isClickEvent(nowX, nowY)){
					CardView selctedCardView = null;
					for(int i=0;i<getChildCount();i++){
						CardView cv = (CardView)getChildAt(i);
						double clickedDegree = getDegreeFromCartesian(nowX, nowY, childAxisX, childAxisY);
						clickedDegree = clickedDegree%360;
						if(clickedDegree > 180.0){
							clickedDegree = clickedDegree - 360.0;
						}else if(clickedDegree < -180.0){
							clickedDegree = 360.0+ clickedDegree;
						}

						Log.i("aaa", "degree="+clickedDegree);
						double radius = Math.sqrt((nowX-childAxisX)*(nowX-childAxisX)+(nowY-childAxisY)*(nowY-childAxisY));
						if(cv.isClicked(clickedDegree, radius, childAxisX - cv.getLeft(), childAxisY-cv.getTop())){
							selctedCardView = cv;
						}
					}
					if(selctedCardView != null){
						if(selctedCardView == pickView(Math.toDegrees(sumRadian), 0.5)){
							if(mOnItemSelectedListener!=null){
								mOnItemSelectedListener.onItemSelected(selctedCardView);
							}
						}

						destDegree = -selctedCardView.getInitRotateDegree();
						animateCardViews(Math.toDegrees(sumRadian), destDegree);
					}

					break;
				}

				double sumDegree = Math.toDegrees(sumRadian);
				double ceilDegree = Math.ceil(sumDegree / mUnitDegree)*mUnitDegree;
				double floorDegree = Math.floor(sumDegree / mUnitDegree)*mUnitDegree;

				if(sumDegree < 0){
					if(Math.signum(sumDegree-(ceilDegree+floorDegree)/2) < 0.0){
						destDegree = floorDegree;
					}else if(Math.signum(sumDegree-(ceilDegree+floorDegree)/2) > 0.0){
						destDegree = ceilDegree;
					}else{
						break;
					}
				}

				animateCardViews(sumDegree, destDegree);
				break;
		}
		return true;
	}

	private void doBringToFront(double degree, double rate){
		View child = pickView(degree, rate);
		if(child == null){
			return;
		}
		bringChildToFront(child);

	}

	private View pickView(double degree, double rate){
		View pickChild = null;
		double unitDegree = 30.0;
		for(int i=0;i<getChildCount(); i++){
			CardView cv = (CardView)getChildAt(i);
			if(cv.isSelected(degree, unitDegree, rate)){
				pickChild = cv;
			}
		}

		return pickChild;
	}

	private boolean isClickEvent(float nowX, float nowY){
		return Math.sqrt((nowX-startX)*(nowX-startX) + (nowY-startY)*(nowY-startY)) < mClickDistance; 
	}

	private boolean isValidArea(double degree, double from, double to){
		double newDegree = degree%360;
		if(newDegree > 180.0){
			newDegree = newDegree - 360.0; 
		}else if(newDegree < -180.0){
			newDegree = newDegree + 360.0;
		}
		if(from <= newDegree && to >= newDegree){
			return true;
		}

		return false;
	}

	private void initTouchEvent(MotionEvent event){
		this.vTracker.recycle();
		vTracker = null;

		this.vTracker = VelocityTracker.obtain();
		beforeX = event.getX();
		beforeY = event.getY();
	}

	private double getRotatedDegree(float x1, float y1, float x2, float y2, float cx, float cy){
		double startDegree = 0.0;
		double endDegree = 0.0;
		double rotateDegree = 0.0;
		startDegree = getDegreeFromCartesian(x1, y1, cx, cy);
		endDegree = getDegreeFromCartesian(x2, y2, cx, cy);
		rotateDegree = endDegree - startDegree;
		if(rotateDegree < -180.0){
			rotateDegree = 360 + rotateDegree ;
		}else if(rotateDegree > 180.0){
			rotateDegree = rotateDegree - 360;
		}

		return rotateDegree;
	}

	private double getDegreeFromCartesian(float nowX, float nowY, float centerX, float centerY){
		double nowRadian = 0.0;
		double nowDegree = 0.0;

		if(nowX - centerX > 0){
			nowRadian = Math.atan((nowY-centerY)/(nowX-centerX)) + Math.PI/2;
		}else if(nowX - centerX < 0){
			nowRadian = Math.atan((nowY-centerY)/(nowX-centerX)) + Math.PI/2*3;
		}
		nowDegree = Math.toDegrees(nowRadian);

		return nowDegree;
	}

	private void animateCardViews(final double fromDegree, final double destDegree){

		Thread animationThread = new Thread(new Runnable() {
			final long DURATION_TIME = 100;
			@Override
			public void run() {
				// TODO Auto-generated method stub
				long startTime = System.currentTimeMillis();
				long endTime = startTime + DURATION_TIME;
				long nowTime = System.currentTimeMillis();
				while((nowTime=System.currentTimeMillis()) <= endTime){
					long interpolatedTime = nowTime - startTime;
					final double sumDegree = fromDegree + (destDegree - fromDegree)/DURATION_TIME * interpolatedTime;

					sumRadian = Math.toRadians(sumDegree);
					postRedraw(sumDegree);
				}

				if(sumRadian != destDegree){
					sumRadian = Math.toRadians(destDegree);

					postRedraw(destDegree);
				}
			}
		});
		animationThread.start();
	}

	private void postRedraw(final double degree){
		postInvalidate();
		post(new Runnable() {

			@Override
			public void run() {
				// TODO Auto-generated method stub
				doBringToFront(degree, 0.5);
			}
		});

	}

	@Override
	protected boolean getChildStaticTransformation(View child,
			Transformation t) {
		// TODO Auto-generated method stub
		final int childWidth = child.getWidth();
		final int childHeight = child.getHeight();

		childAxisX = child.getLeft() + child.getWidth()/2;
		childAxisY = child.getTop()+ child.getHeight();

		float alpha = 1.0f;
		CardView cv = null;
		if(child instanceof CardView){
			cv = (CardView)child;
			cv.setDegree(Math.toDegrees(sumRadian));
			if(!cv.isSelected(Math.toDegrees(sumRadian), mUnitDegree, 0.5)){
				alpha = 1.0f;
			}else{
				alpha = 1.0f;
			}
		}

		Camera mCamera = new Camera();
		t.setTransformationType(Transformation.TYPE_MATRIX);

		mCamera.save();
		final Matrix matrix = t.getMatrix();
		t.setAlpha(alpha);

		mCamera.rotateZ((float)-cv.getRotatedDegree());         
		mCamera.getMatrix(matrix);    

		matrix.preTranslate(-(childWidth/2), -(childHeight));
		matrix.postTranslate((childWidth/2), (childHeight));  

		mCamera.restore();

		return true;
	}

	@Override
	protected void onAttachedToWindow() {
		// TODO Auto-generated method stub
		super.onAttachedToWindow();
		doBringToFront(Math.toDegrees(sumRadian), 0.5);
	}

	public double getNowRadian() {
		return nowRadian;
	}
	public void setNowRadian(double nowRadian) {
		this.nowRadian = nowRadian;
	}

	public interface OnItemSelectedListener{
		public void onItemSelected(View v);
	}

	public void setMaxDegree(double degree){
		mMaxDegree = degree;
		if(getChildCount()>1){
			mUnitDegree = mMaxDegree / (getChildCount()-1);
		}else{
			mUnitDegree = mMaxDegree;
		}

		for(int i=0; i < getChildCount() ; i++){
			double initDegree = mUnitDegree*(getChildCount()-1-i);
			CardView cv = (CardView)getChildAt(i);
			cv.setInitRotatedDegree(initDegree);
		}
		sumRadian = 0.0;
		this.invalidate();
	}

	public OnItemSelectedListener getmOnItemSelectedListener() {
		return mOnItemSelectedListener;
	}
	public void setmOnItemSelectedListener(
			OnItemSelectedListener mOnItemSelectedListener) {
		this.mOnItemSelectedListener = mOnItemSelectedListener;
	}
}